{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<center>\n",
    "<img src=\"../../img/ods_stickers.jpg\">\n",
    "## Открытый курс по машинному обучению. Сессия № 2\n",
    "\n",
    "### <center> Автор материала: Анастасия Малюгина (@malugina)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## <center> Индивидуальный проект по анализу данных \n",
    "### <center> Анализ новостей для предсказания фондового рынка"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "import re\n",
    " \n",
    "from sklearn.base import BaseEstimator, TransformerMixin\n",
    "from sklearn.ensemble import GradientBoostingClassifier\n",
    "from sklearn.neighbors import KNeighborsClassifier\n",
    "from sklearn.linear_model import LogisticRegression\n",
    "from sklearn.metrics import r2_score, roc_auc_score\n",
    "from sklearn.model_selection import train_test_split, GridSearchCV, validation_curve, StratifiedKFold, cross_val_score, learning_curve\n",
    "from sklearn.feature_extraction.text import TfidfTransformer, CountVectorizer, TfidfVectorizer\n",
    "from sklearn.decomposition import PCA\n",
    "from sklearn.pipeline import make_pipeline, Pipeline\n",
    "from sklearn.preprocessing import MaxAbsScaler\n",
    "from sklearn.svm import SVC\n",
    "\n",
    "%matplotlib inline\n",
    "from matplotlib import pyplot as plt\n",
    "import pylab \n",
    "from pylab import rcParams\n",
    "import seaborn as sns\n",
    "\n",
    "from tqdm import tqdm_notebook\n",
    "\n",
    "from scipy import stats as stats\n",
    "from scipy.stats.stats import pearsonr \n",
    "\n",
    "RANDOM_STATE = 100"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. Описание набора данных и признаков"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "В данном наборе данных предлагается рассмотреть связь между текстами ежедневных новостей и изменением индекса [Доу Джонса](https://ru.wikipedia.org/wiki/Промышленный_индекс_Доу_—_Джонса) (DJIA). Этот индекс отражает состояние американской экономики, и умение его предсказыть позволяет компаниям принимать правильные решения о покупке или продаже акций и ценных бумаг. Индекс расситывается как средняя цена акций 30 крупнейших американских компаний. Определенные новости об этих компаниях способны изменить стоимость их ценных бумаг, и, как следствие, изменить индекс. В данной работе предстоит проверить, насколько тексты новостей влияют на изменение этого индекса.\n",
    "\n",
    "Данные взяты с [Kaggle](https://www.kaggle.com/aaron7sun/stocknews/data), копия – [тут](https://drive.google.com/file/d/1OpKHm9iDcKJwSKUTbD0SFLDZ3MbiJBGB/view?usp=sharing).\n",
    "\n",
    "Давайте посмотрим на исходный набор файлов: \n",
    "\n",
    "### 1) RedditNews.csv - Список новостей по датам\n",
    "- Date - дата выхода новости, \n",
    "- News - текст новости\n",
    "- Одна дата - несколько новостей"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "news = pd.read_csv('RedditNews.csv')\n",
    "news.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2) DJIA_table.csv - Индекс Доу Джонса по дням\n",
    "\n",
    "- Date - дата\n",
    "- Open - значение индекса DJIA на момент открытия торгов\n",
    "- Close - значение индекса DJIA на момент закрытия торгов\n",
    "- High, Low - наибольшее и наименьшее значение индекса DJIA в течение указаного дня\n",
    "- Volume - объём торгов в течение дня\n",
    "- Adj Close - скорректированное значение индекса DJIA, в который были внесены изменения, включая любые распределения и корпоративные действия, которые произошли в любое время до открытия следующего дня. В этом наборе данных значение этого поля во всех строках равно Close, то есть оно не несет никакой новой информации. Сразу удалим его"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "djia = pd.read_csv('DJIA_table.csv')\n",
    "djia.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "djia[djia['Adj Close'] - djia['Close'] !=0 ].shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "del djia['Adj Close']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3) Combined_News_DJIA.csv \n",
    "\n",
    "Это аналог первой таблицы, уже сгруппированный по датам со следующими полями:\n",
    "- Date - дата\n",
    "- Label - метка соответствующая положиельному закрытию торгов (1 - положительное, 0 - отрицательное)\n",
    "- Top1 - Top25 - тексты новостей за указанную дату\n",
    "\n",
    "[Ссылка](https://drive.google.com/file/d/1_qMektcsrrRZrg6S1-dC3dCVJ-mgY_PQ/view?usp=sharing) на данные. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "news_comb = pd.read_csv('Combined_News_DJIA.csv')\n",
    "news_comb.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Метка в таблице Combined_News_DJIA.csv предполагает решение задачи классификации, в плюсе или минусе закроются торги. Но, на мой взгляд, было бы интереснее и логичнее решить задачу регрессии и оценить то, насколько велик будет плюс или минус, так как для принятия решения о финансовых операциях важно знать, поднимется ли этот индекс на половину пункта или на сотни.\n",
    "\n",
    "Поэтому соединим таблицы news_comb и djia, и подсчитаем разницу в закрытии и открытии. В дальнейшем попробуем рассмотреть абсолютную и относительную разницу в качестве целевой переменной. Рассматривать само значение индекса Доу Джонса в качестве целевой переменной не стоит, так как новости влияют на его изменение относительно начала торгов. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = pd.merge(djia, news_comb, on='Date')\n",
    "data['Diff_abs'] = data['Close'] - data['Open']\n",
    "data['Diff_rel'] = (data['Close'] - data['Open']) / data['Open']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. Первичный анализ данных"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data.describe()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Проверим заполненность полей:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data.isnull().sum()[data.isnull().sum()>0]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Видим, что в трёх днях было меньше новостей, чем обычно. Можно было бы исключить эти записи, но, на мой взгляд, их надо оставить, так как тексты всё равно будем объединять, а также попробуем проверить влияние вчерашних новостей на сегодняшний курс. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# отсортируем по датам:\n",
    "data.sort_values(by='Date', axis=0, ascending=True, inplace=True)\n",
    "# переведем тип столбца\n",
    "data['Date'] = data['Date'].astype(dtype='datetime64[ns]')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.figure(1, figsize=(20, 10))\n",
    "\n",
    "plt.subplot(311)\n",
    "plt.plot(data['Date'], data['Close'])\n",
    "\n",
    "plt.subplot(312)\n",
    "plt.plot(data['Date'], data['Diff_abs'])\n",
    "\n",
    "plt.subplot(313)\n",
    "plt.plot(data['Date'], data['Diff_rel'])\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "По поводу выбросов сделаю предположение: их нет, данные собраны автоматически, ошибки при ручном вводе исключены, и график (первый) показывает адекватные данные"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Посмотрим, не пропущены ли какие либо дни, для этого посчитаем интервалы между текущим и предыдущим днём:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "intervals = (data['Date'] - data['Date'].shift(1)).apply(lambda x: x.days)\n",
    "print(intervals.value_counts())\n",
    "plt.figure(figsize=(20, 10))\n",
    "plt.plot(data['Date'][:100], intervals[:100])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Очевидно, что пропуски - это выходные и изредка праздники, то есть дни в которые торги на бирже не проходят. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "rcParams['figure.figsize'] = 5, 3\n",
    "data['Diff_abs'].plot(kind='hist', bins=100)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Если смотреть на ранные с точки зрения классификации. то распределение целевой переменной будет следующим:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data['Label'].value_counts().plot(kind='bar', label='Рост DJIA')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Присутствует небольшой дисбаланс классов"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Проверка на нормальность"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Критерий Шапиро-Уилка:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(stats.shapiro(data['Close']))\n",
    "print(stats.shapiro(data['Diff_abs']))\n",
    "print(stats.shapiro(data['Diff_rel']))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Q-Q plot: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "stats.probplot(data['Diff_abs'], dist=\"norm\", plot=pylab)\n",
    "pylab.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Выводы:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. Первичный визуальный анализ данных"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Частично визуальный анализ, касающийся целевой переменной, присутствовал в предыдущем пункте. Так как признаки - это тексты и даты, то применим к ним преобразования"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "1. Выделим из даты год, год-месяц, месяц, день недели."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data['Year'] = data['Date'].apply(lambda x: x.year)\n",
    "data['Month'] = data['Date'].apply(lambda x: x.month)\n",
    "min_year = data['Year'].min()\n",
    "data['Year_Month'] = data['Date'].apply(lambda x: (x.year - min_year)*12 + x.month)\n",
    "data['weekday'] = data['Date'].apply(lambda x: x.weekday())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "2.1. А теперь соединим все тексты по годам, посчитаем tf-idf и выберем топ-20 слов, наиболее характерных для каждого года.\n",
    "Для этого соединим все тексты дня в один, а затем сгруппируем по годам:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# очищаем тексты от всего, кроме слов\n",
    "def filterNews(s):\n",
    "    if type(s) == str:\n",
    "        s = s.lower()\n",
    "        s = re.sub(r'^b[\\'\\\"]','',s)\n",
    "        s = re.sub(r'[\\n]','',s)\n",
    "        s = re.sub(r'[^a-z ]','',s)\n",
    "    return s"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Склеенные 25 новостей\n",
    "data['Combined_news'] = data.filter(regex=('Top.*'))\\\n",
    "    .apply(lambda x: ''.join(x.apply(lambda y: filterNews(y)).values.astype(str)), axis=1) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#новости, сгруппированные по годам\n",
    "yearly_news = data.groupby(['Year'])['Combined_news'].apply(lambda x: ''.join(x.values.astype(str)))\n",
    "yearly_news"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# преобразуем текты в мешок слов\n",
    "# max_df=4 необходимо, чтобы отсеить высокочастотные слова, встречающиеся во многих текстах\n",
    "# иначе получим максимальный tf-idf у слов типа \"the\", \"of\", \"to\"\n",
    "# Это связанно с тем, что при расчете idf в библиотеке sklearn используется формула, \n",
    "# в которой к логирифму частного прибаляется единица. Таким образом, у слов, которые часто\n",
    "# встречаются в текстах будет максимальное значение tf-idf\n",
    "\n",
    "def get_top_words(data, n_top=10, max_df=None):\n",
    "    cnt = CountVectorizer(max_df=max_df)\n",
    "    a = cnt.fit_transform(data)\n",
    "    feature_array = np.array(cnt.get_feature_names())\n",
    "\n",
    "    tfidf = TfidfTransformer()\n",
    "    a = tfidf.fit_transform(a)\n",
    "    #print(pd.DataFrame(a.toarray(),columns=feature_array))\n",
    "\n",
    "    # посмотрим на n_top самых актуальных слов года\n",
    "    ar = [] #  и запишем их в этот массив\n",
    "    for i in range(len(data)):\n",
    "        tfidf_sorting = np.argsort(a[i].toarray()).flatten()[::-1] # ключи для сортировки\n",
    "        top_keys = feature_array[tfidf_sorting][:n_top] # сами слова\n",
    "        top_vals = np.array([a[i].toarray()[0][tfidf_sorting][:n_top]]) # значения их tf-idf\n",
    "        #print(i,': ',top_n, a[i].toarray()[0][tfidf_sorting][:n_top])\n",
    "        ar.append(pd.DataFrame(top_vals,columns=top_keys,index=[data.index[i]])) # собираем в один массив\n",
    "\n",
    "    top_words = pd.concat(ar).fillna(0) # и соединяем все в один датафрейм с общими колонками   \n",
    "    return top_words\n",
    "\n",
    "top_words = get_top_words(yearly_news, 10, 4) # посмотрим на 10 самых актуальных слов года\n",
    "top_words"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Наконец, можно приступить к визуализации! "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. По годам.\n",
    "Построим тепловую карту самих коэффициентов и корреляции слов."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "rcParams['figure.figsize'] = 5, 30\n",
    "sns.heatmap(top_words.T, annot=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "На этом графике очень наглядно видны тенденции: про Сноудена активно говорили в 2013 году, потом в 2014 меньше (но тоже часто упоминали), в 2015 еще реже, а в 2016 уже и не попало его имя в топ-10 слов. Одновременно идет чуть более слабая тенденция для слова Edward (это же имя Сноудена, и оно также упоминалось в новостях в 2014 и потом пошло на спад). Другое заметное пятно - это упоминание isis (исламское государство) в 2014-2016 годах. Если увеличить картинку, и вчитаться, то можно увидеть много знакомых новостей: Донецк обсуждали в 2014, Грузию, Грузию-Россию и Саакашвили в 2008. Вот тут возникает мысль построить матрицу коррелиции и посмотреть, какие новости обсуждали одновременно:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "rcParams['figure.figsize'] = 25, 25\n",
    "sns.heatmap(top_words.corr(), annot=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Но эта матрица кажется мне не слишком выразительной, многие слова, активно упоминаемые в один год, нельзя назвать взаимосвязанными. Год - достаточно большой период. поэтому тут много сильно скоррелированных слов. Попробуем построить по месяцам и дням."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "monthly_news = data.groupby(['Year_Month'])['Combined_news'].apply(lambda x: ''.join(x.values.astype(str)))\n",
    "\n",
    "top_words = get_top_words(monthly_news, 3, max_df=0.9) # посмотрим на 1 самых актуальных слов месяца\n",
    "top_words.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "rcParams['figure.figsize'] = 25, 30\n",
    "sns.heatmap(top_words.T, annot=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "rcParams['figure.figsize'] = 25, 25\n",
    "sns.heatmap(top_words.corr(), annot=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Посмотрим, что предаставлюят из себя пары сильно скоррелированных слов:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "corr = top_words.corr()\n",
    "corr = corr - np.tril(corr)\n",
    "corr.stack().nlargest(20)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Мы получили пары слов, упоминаемых одновременно: про апокалипсис говорили тогда же, когда и про Майя, Кадаффи - Ливия и т.д."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "### По дням:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Строить графики нет смысла, т.к. визуально их не оценить. Тут можно перейти к связи слов и индекса ДоуДжонса: какие слова в новостях совпали с повышением и с понижением этого индекса? "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cnt = CountVectorizer(ngram_range=(1,2), stop_words='english', min_df=3) #min_df=3 - чтобы исключить слова с ошибками\n",
    "words = cnt.fit_transform(data['Combined_news'])\n",
    "feature_array = np.array(cnt.get_feature_names())\n",
    "\n",
    "tfidf = TfidfTransformer()\n",
    "words_tfidf = tfidf.fit_transform(words)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "corr = np.empty(words_tfidf.shape[1])\n",
    "targ = np.array([data['Diff_abs']]).T\n",
    "for i in tqdm_notebook(range(words_tfidf.shape[1])):#a1.shape[1]-1\n",
    "    corr[i] = pearsonr(words_tfidf[:,i].toarray(), targ)[0][0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "corr = pd.Series(corr, index=feature_array)\n",
    "corr.nlargest(15)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Обрантая корреляция (частое упротребление каких слов приводит к понижению индекса) :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "(-corr).nlargest(15)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Корелляция частоты слов и изменений биржевого индекса слабая, менее 0.12. Значит, каждое отдельно взятое слово (или пара слов) не влияет существено на целевую переменную."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Построим простую линейную модель и посмотрим на слова с самыми высокими по модую коэффициентами:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# функция взята из лекции по линейным моделям\n",
    "def visualize_coefficients(classifier, feature_names, n_top_features=25):\n",
    "    # get coefficients with large absolute values \n",
    "    coef = classifier.coef_.ravel()\n",
    "    positive_coefficients = np.argsort(coef)[-n_top_features:]\n",
    "    negative_coefficients = np.argsort(coef)[:n_top_features]\n",
    "    interesting_coefficients = np.hstack([negative_coefficients, positive_coefficients])\n",
    "    # plot them\n",
    "    plt.figure(figsize=(15, 5))\n",
    "    colors = [\"red\" if c < 0 else \"blue\" for c in coef[interesting_coefficients]]\n",
    "    plt.bar(np.arange(2 * n_top_features), coef[interesting_coefficients], color=colors)\n",
    "    feature_names = np.array(feature_names)\n",
    "    plt.xticks(np.arange(1, 1 + 2 * n_top_features), feature_names[interesting_coefficients], rotation=60, ha=\"right\");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.svm import SVC"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "model =  LogisticRegression()\n",
    "model.fit(words_tfidf, data['Label'])\n",
    "visualize_coefficients(model, feature_array, 25)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Так как модель линейная, то \"синие\" слова связаны с повышением биржевого индекса, а \"красные\" - с понижением. Посмотрим, насколько можно умньшить размерность, сохранив 90% дисперсии:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pca = PCA().fit(words_tfidf.toarray())\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "plt.figure(figsize=(10,7))\n",
    "plt.plot(np.cumsum(pca.explained_variance_ratio_), color='k', lw=2)\n",
    "plt.xlabel('Number of components')\n",
    "plt.ylabel('Total explained variance')\n",
    "#plt.xlim(0, 1263)\n",
    "plt.yticks(np.arange(0, 1.1, 0.1))\n",
    "plt.axvline(1600, c='b')\n",
    "plt.axhline(0.9, c='r')\n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 5. Выбор метрики"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Так как классы несколько несбалансированы, а также для оценки полезности модели будем использовать ROC-AUC "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 6. Выбор модели"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Так как обрабатывать будем разреженные данные, то для этого отлично подходят линейные модели. Для работы с текстом часто используют метод опорных векторов. Также сделаем предположение, что в дни с похожили новостями будут и похожие изменения, поэтому попробуем метод ближайших соседей. И градиентный бустинг, ну как его не проверить) Проверим все методы, сравним их эффективность."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 7. Предобработка данных"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 7.1. Разобъём данные на обучающую и тестовую выборки:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Как уже делалось ранее, склеим тексты новостей в одно поле, оставив лишь слова. В раздельном хранении нет смысла, так как новости (поля Top1-Top25) не упорядочены по значимости, категории или как то ещё. И отдельное преобразование не даст результата, так как в одном признаке будут разные ничем не связанные тексты."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data['Combined_news'] = data.filter(regex=('Top.*'))\\\n",
    "    .apply(lambda x: ''.join(x.apply(lambda y: filterNews(y)).values.astype(str)), axis=1) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Наиболее логичным разбиением для этой задачи будет разбиение по дате: до - обучающая, после - тестовая. Перемешивание может привести к утечке данных, в итоге получив завышенный результат.\n",
    "Треть данных оставим для тестовой выборки"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_train, X_test, y_train, y_test = train_test_split(data.drop(['Label'],axis=1), data['Label'], test_size=0.33)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\"Мешок слов\" следует обучать только на тренировочной выборке, применив преобразование к тестовой, чтобы не заглядывать вперед. \n",
    "И также с TF_IDF. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "count_vect = CountVectorizer(ngram_range=(1,2), stop_words='english', min_df=3)\n",
    "count_vect.fit(X_train['Combined_news'])\n",
    "X_train_text = count_vect.transform(X_train['Combined_news'])\n",
    "X_test_text = count_vect.transform(X_test['Combined_news'])\n",
    "\n",
    "tfidf = TfidfTransformer()\n",
    "tfidf.fit(X_train_text)\n",
    "X_train_text = tfidf.transform(X_train_text)\n",
    "X_test_text = tfidf.transform(X_test_text)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "В дальнейшем будем использовать подобное последовательное преобразование: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pipe_cnt_tfidf = Pipeline([\n",
    "    ('count', CountVectorizer(ngram_range=(1,2), stop_words='english', min_df=3)),\n",
    "    ('tfidf', TfidfTransformer()),\n",
    "    ('scaler', MaxAbsScaler()),\n",
    "    ('classifier',LogisticRegression())\n",
    "])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Стоит заметить, что данный датасет можно рассмативать и как временной ряд, прогнозируя значения последовательно, опираясь на предыдущие. Но основная задачу тут - рассмотреть свять между текстами новостей и их влиянием на индекс ДоуДжонса."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 8. Кросс-валидация и настройка гиперпараметров модели"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Создадим Pipeline, чтобы отдельно преобразовывать в tfidf блоки, предназначенные для обучения и для валидации. Для масштаброввания использую MaxAbsScaler, который специально разработан для преобразования разреженных матриц.\n",
    "Я не удержалась и к логистической регрессии добавила метод опорных векторов и градиентный бустинг (ну как без него), чтобы сравнить результаты. Число фолдов равно 3, это почти минимум, чтобы оценить среднеквадратичное отклонение, а при большем числе фолдов на некоторых алгоритмах ноут перегревается и отключается ¯\\\\_(ツ)_/¯"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pipe_cnt_tfidf = Pipeline([\n",
    "    ('count', CountVectorizer(ngram_range=(1,2), stop_words='english', min_df=3)),\n",
    "    ('tfidf', TfidfTransformer()),\n",
    "    ('scaler', MaxAbsScaler()),\n",
    "    ('classifier',LogisticRegression())\n",
    "])\n",
    "kf = StratifiedKFold(n_splits=3, shuffle=True, random_state=99)\n",
    "param_grid = [\n",
    "    {\n",
    "        'classifier': [LogisticRegression()],\n",
    "        'classifier__C': np.logspace(-6,2,12)\n",
    "    },\n",
    "    {\n",
    "        'classifier': [LogisticRegression(solver='newton-cg')],\n",
    "        'classifier__max_iter': [5,10,50,100]\n",
    "    },\n",
    "    {\n",
    "        'classifier': [LinearSVC()],\n",
    "        'classifier__C': np.logspace(-6,2,8)\n",
    "    },\n",
    "    {\n",
    "        'classifier': [KNeighborsClassifier()],\n",
    "        'classifier__n_neighbors': [3,7,15,30]\n",
    "    }\n",
    "\n",
    "]\n",
    "\n",
    "grid = GridSearchCV(pipe_cnt_tfidf, cv=kf, n_jobs=1, param_grid=param_grid, scoring='roc_auc')\n",
    "grid.fit(X_train['Combined_news'], y_train)\n",
    "grid.best_score_"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "На тренировочной части оценка близка к 1, а на валидационной - 0.5. Это говорит о переобучении модели, даже при очень малых значениях С (см. далее кривые валидации)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# преобразование разреженной матрицы в массив, чтобы его можно было встроить в pipeline\n",
    "class ToArrayTransformer(BaseEstimator, TransformerMixin):\n",
    "\n",
    "    def transform(self, X, y=None, **fit_params):\n",
    "        return X.toarray()\n",
    "\n",
    "    def fit_transform(self, X, y=None, **fit_params):\n",
    "        self.fit(X, y, **fit_params)\n",
    "        return self.transform(X)\n",
    "\n",
    "    def fit(self, X, y=None, **fit_params):\n",
    "        return self"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pipe_cnt_tfidf_gb = Pipeline([\n",
    "    ('count', CountVectorizer(ngram_range=(1,2), stop_words='english', min_df=3)),\n",
    "    ('tfidf', TfidfTransformer()),\n",
    "    ('toarray', ToArrayTransformer()),\n",
    "    #('PCA',PCA(n_components=1000)),\n",
    "    ('classifier',GradientBoostingClassifier(max_features='log2', subsample=0.9, random_state=99))\n",
    "])\n",
    "param_grid = [\n",
    "    {\n",
    "        'classifier__n_estimators': [10,100,500,1000]\n",
    "    },\n",
    "]\n",
    "\n",
    "grid = GridSearchCV(pipe_cnt_tfidf_gb, cv=kf, n_jobs=1, param_grid=param_grid, scoring='roc_auc')\n",
    "grid.fit(X_train['Combined_news'], y_train)\n",
    "grid.best_score_"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "grid.cv_results_['mean_test_score']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "grid.cv_results_"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "И построим сразу для бустринг кривую валидации, чтобы не запускать все заново:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "test_res = np.array([grid.cv_results_['split0_test_score'],grid.cv_results_['split1_test_score'],grid.cv_results_['split2_test_score']]).T;\n",
    "train_res = np.array([grid.cv_results_['split0_train_score'],grid.cv_results_['split1_train_score'],grid.cv_results_['split2_train_score']]).T;\n",
    "plot_with_err([10,100,500,1000], test_res, label='training scores')\n",
    "plot_with_err([10,100,500,1000], train_res, label='validation scores')\n",
    "plt.xlabel('N'); plt.ylabel('ROC AUC')\n",
    "#plt.xscale('log')\n",
    "plt.legend()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Как видно, результаты неудовлетворительные, сравнимые с константным классификатором, т.к. roc_auc колеблется около 0.5 (+/- 0.03) Хотя на графике бустинга заметна небольшая тенденция по улучшению качества при увеличении числа деревьев."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 9. Создание новых признаков и описание этого процесса"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 9.1. Текст вчерашней новости (Tfidf)\n",
    "В описании данных нет информации о точном времени выхода новости, и можно предположить, что новости, опубликованные вчера после закрытия торгов на бирже, повлияют на курс только сегодня. Также можно предположить, что эффект от новости будет распространяться и на следующий день"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data['Combined_news_prev'] = data['Combined_news'].shift(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pipe_cnt_tfidf = Pipeline([\n",
    "    ('count', CountVectorizer(ngram_range=(1,2), stop_words='english', min_df=3)),\n",
    "    ('tfidf', TfidfTransformer()),\n",
    "    ('scaler', MaxAbsScaler()),\n",
    "    ('classifier',LogisticRegression())\n",
    "])\n",
    "kf = StratifiedKFold(n_splits=3, shuffle=True, random_state=99)\n",
    "param_grid = [\n",
    "    {\n",
    "        'classifier': [LogisticRegression()],\n",
    "        'classifier__C': np.logspace(-6,2,12)\n",
    "    }]\n",
    "\n",
    "grid = GridSearchCV(pipe_cnt_tfidf, cv=kf, n_jobs=1, param_grid=param_grid, scoring='roc_auc')\n",
    "grid.fit(data['Combined_news_prev'][1:], data['Label'][1:])\n",
    "grid.best_score_"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 9.2. Месяц, год, день недели\n",
    "Месяц, год, день недели уже были подготовлены в пункте 3 (при первичном визуальном анализе)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data['DayOfMonth'] = data['Date'].apply(lambda x: x.day)\n",
    "data['WeekOfYear'] = data['Date'].apply(lambda x: x.weekofyear)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data_dates = data[['Date','Year','Month','weekday','Year_Month','DayOfMonth','WeekOfYear']]\n",
    "data_dates.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data_dates = pd.get_dummies(data=data_dates, columns=['Date','Year','Month','weekday','Year_Month','DayOfMonth','WeekOfYear'])\n",
    "data_dates.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "kf = StratifiedKFold(n_splits=3, shuffle=True, random_state=99)\n",
    "gb = GradientBoostingClassifier(max_features='log2', subsample=0.9, random_state=99)\n",
    "grid = GridSearchCV(gb, cv=kf, param_grid={'n_estimators':[100,500,1000]}, scoring='roc_auc')\n",
    "grid.fit(data_dates, data['Label'])\n",
    "grid.best_score_"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "kf = StratifiedKFold(n_splits=3, shuffle=True, random_state=99)\n",
    "lr = LogisticRegression()\n",
    "grid = GridSearchCV(lr, cv=kf, param_grid={'C':np.logspace(-6,2,10)}, scoring='roc_auc')\n",
    "grid.fit(data_dates, data['Label'])\n",
    "grid.best_score_"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Результаты тоже плохи. Смысла добавлять эти признаки нет. Вероятно, может улучшить ситуацию смысловая обработка текстов, разделение новостей на категории и отбор и использование только тех новостей, которые действительно как-то влияют на биржевой индекс. А также выделение сущностей: объекты, действия. Но это тоько перспектива и не входит в данную работу, "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 10. Построение кривых валидации и обучения"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_with_err(x, data, **kwargs): # функция взята из лекции на Хабре\n",
    "    mu, std = data.mean(1), data.std(1)\n",
    "    lines = plt.plot(x, mu, '-', **kwargs)\n",
    "    plt.fill_between(x, mu - std, mu + std, edgecolor='none',\n",
    "    facecolor=lines[0].get_color(), alpha=0.2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Кривая валидации для логистической регрессии: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pipe_cnt_tfidf_lr = Pipeline([\n",
    "    ('count', CountVectorizer(ngram_range=(1,2), stop_words='english', min_df=3)),\n",
    "    ('tfidf', TfidfTransformer()),\n",
    "    ('scaler', MaxAbsScaler()),\n",
    "    ('classifier',LogisticRegression())\n",
    "])\n",
    "C_range = np.logspace(-7,5,10)\n",
    "val_train, val_test = validation_curve(estimator=pipe_cnt_tfidf, \n",
    "                                       X=X_train['Combined_news'], \n",
    "                                       y=y_train, \n",
    "                                       param_name='classifier__C', \n",
    "                                       param_range=C_range, \n",
    "                                       cv=3, \n",
    "                                       scoring='roc_auc')\n",
    "\n",
    "\n",
    "plot_with_err(C_range, val_train, label='training scores')\n",
    "plot_with_err(C_range, val_test, label='validation scores')\n",
    "plt.xlabel(r'$\\alpha$'); plt.ylabel('ROC AUC')\n",
    "plt.xscale('log')\n",
    "plt.legend()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Для метода опорных векторов: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pipe_cnt_tfidf = Pipeline([\n",
    "    ('count', CountVectorizer(ngram_range=(1,2), stop_words='english', min_df=3)),\n",
    "    ('tfidf', TfidfTransformer()),\n",
    "    ('classifier', SVC())\n",
    "])\n",
    "С_range = np.logspace(-6, 5, 10)\n",
    "val_train, val_test = validation_curve(estimator=pipe_cnt_tfidf, \n",
    "                                       X=X_train['Combined_news'], \n",
    "                                       y=y_train, \n",
    "                                       param_name='classifier__C', \n",
    "                                       param_range=С_range, \n",
    "                                       cv=3, \n",
    "                                       scoring='roc_auc')\n",
    "plt.subplot(122)\n",
    "plot_with_err(С_range, val_train, label='training scores')\n",
    "plot_with_err(С_range, val_test, label='validation scores')\n",
    "plt.xlabel('C'); plt.ylabel('ROC AUC')\n",
    "plt.xscale('log')\n",
    "plt.legend()\n",
    "\n",
    "gamma_range = np.logspace(-6, -1, 5)\n",
    "val_train, val_test = validation_curve(estimator=pipe_cnt_tfidf, \n",
    "                                       X=X_train['Combined_news'], \n",
    "                                       y=y_train, \n",
    "                                       param_name='classifier__gamma', \n",
    "                                       param_range=gamma_range, \n",
    "                                       cv=3, \n",
    "                                       scoring='roc_auc')\n",
    "\n",
    "\n",
    "plt.subplot(121)\n",
    "plot_with_err(gamma_range, val_train, label='training scores')\n",
    "plot_with_err(gamma_range, val_test, label='validation scores')\n",
    "plt.xlabel(r'$\\gamma$'); plt.ylabel('ROC AUC')\n",
    "plt.legend()\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "Для градиентного бустинга - выполнена выше."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Кривая обучения:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pipe_cnt_tfidf_lr = Pipeline([\n",
    "    ('count', CountVectorizer(ngram_range=(1,2), stop_words='english', min_df=3)),\n",
    "    ('tfidf', TfidfTransformer()),\n",
    "    ('scaler', MaxAbsScaler()),\n",
    "    ('classifier',LogisticRegression())\n",
    "])\n",
    "\n",
    "train_sizes, train_scores, valid_scores = learning_curve(estimator=pipe_cnt_tfidf_lr, \n",
    "                                                         X=X_train['Combined_news'], \n",
    "                                                         y=y_train, \n",
    "                                                         cv=3, \n",
    "                                                         scoring='roc_auc', \n",
    "                                                         train_sizes=np.linspace(0.1, 1.0, 5))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.figure()\n",
    "\n",
    "plt.xlabel(\"Training examples\")\n",
    "plt.ylabel(\"Score\")\n",
    "train_scores_mean = np.mean(train_scores, axis=1)\n",
    "train_scores_std = np.std(train_scores, axis=1)\n",
    "valid_scores_mean = np.mean(valid_scores, axis=1)\n",
    "valid_scores_std = np.std(valid_scores, axis=1)\n",
    "plt.grid()\n",
    "\n",
    "plt.fill_between(train_sizes, train_scores_mean - train_scores_std,\n",
    "                 train_scores_mean + train_scores_std, alpha=0.1,\n",
    "                 color=\"r\")\n",
    "plt.fill_between(train_sizes, valid_scores_mean - valid_scores_std,\n",
    "                 valid_scores_mean + valid_scores_std, alpha=0.1, color=\"g\")\n",
    "plt.plot(train_sizes, train_scores_mean, 'o-', color=\"r\",\n",
    "         label=\"Training score\")\n",
    "plt.plot(train_sizes, valid_scores_mean, 'o-', color=\"g\",\n",
    "         label=\"Cross-validation score\")\n",
    "\n",
    "plt.legend(loc=\"best\")\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Результаты одинаково плохи, как при малом числе примеров, так и при большом. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 11. Прогноз для тестовой или отложенной выборки"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pipe_cnt_tfidf_lr = Pipeline([\n",
    "    ('count', CountVectorizer(ngram_range=(1,2), stop_words='english', min_df=2)),\n",
    "    ('tfidf', TfidfTransformer()),\n",
    "    ('scaler', MaxAbsScaler()),\n",
    "    ('classifier',LogisticRegression())\n",
    "])\n",
    "\n",
    "pipe_cnt_tfidf_lr.fit(X_train['Combined_news'], y_train)\n",
    "pred = pipe_cnt_tfidf_lr.predict_proba(X_test['Combined_news'])\n",
    "roc_auc_score(y_test, pred[:,1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pipe_cnt_tfidf_gb = Pipeline([\n",
    "    ('count', CountVectorizer(ngram_range=(1,2), stop_words='english', min_df=3)),\n",
    "    ('tfidf', TfidfTransformer()),\n",
    "    ('toarray', ToArrayTransformer()),\n",
    "    ('classifier',GradientBoostingClassifier(n_estimators=1200, max_features='log2', subsample=0.9, random_state=99))\n",
    "])\n",
    "\n",
    "pipe_cnt_tfidf_gb.fit(X_train['Combined_news'], y_train)\n",
    "pred = pipe_cnt_tfidf_gb.predict_proba(X_test['Combined_news'])\n",
    "roc_auc_score(y_test, pred[:,1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Результаты проверки для отложенной выборки оказались сравнимы с результатами кроссвалидации: roc_auc около 0.5, что говорит о бесполезном классификаторе. Возможно, можно было бы еще поиграть с настройками классификаторов, но факт того, что все классификаторы не вышли за пределы даже 0.55, говорит о том, что связь между признаками и целевой переменной отсутствует."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 12. Выводы"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Полученные модели не дали адекватных прогнозов, ценность решения - нулевая, и, как следствие, отсутствие возможных применений. Это говорит о том, что в таком виде использовать новости для прогноза биржевого индекса нельзя. Возможно, если бы данные имели другой вид, например, было бы указано время публикации новости и приведены поминутные изменения индекса (а лучше - курса акций составляющих компаний). Если же говорить об анализе текста новостей, то стоит провести лексический анализ, например с помощью NLTK. "
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
